package org.budo.graph.service.util;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.budo.graph.config.BudoGraphConfigurer;
import org.budo.support.config.processor.AbstractBudoConfigProcessor;
import org.budo.support.lang.util.MapUtil;
import org.budo.support.lang.util.ReflectUtil;
import org.budo.support.lang.util.StringUtil;
import org.budo.support.servlet.util.QueryStringUtil;
import org.budo.support.slf4j.Slf4j;
import org.budo.support.spring.context.aware.BudoApplicationContextAware;
import org.budo.support.spring.util.SpringUtil;
import org.slf4j.Logger;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;

import com.alibaba.druid.support.json.JSONUtils;
import com.alibaba.dubbo.rpc.service.EchoService;
import com.alibaba.dubbo.rpc.service.GenericService;

/**
 * @author limingwei
 * @date 2016年6月16日 上午8:50:27
 */
public class DubboEchoUtil {
    private static final Logger log = Slf4j.getLogger();

    private static final Class<?> REGISTRY_SERVICE = null;

    public static String writeDubboEcho(ApplicationContext applicationContext, String url) {
        Map<String, String> parameters = QueryStringUtil.queryStringToStringMap(url);
        String echo = parameters.get("echo");

        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        List<String> lines = new ArrayList<String>();

        String[] beanNames = applicationContext.getBeanDefinitionNames();
        for (String beanName : beanNames) {
            Object beanInstance = SpringUtil.getBean(applicationContext, beanName);
            if (null == beanInstance) {
                continue;
            }

            String interfaceNames = getBeanInterfaceNames(beanInstance) + "";
            if (null == interfaceNames || interfaceNames.trim().isEmpty() || "null".equalsIgnoreCase(interfaceNames)) {
                log.error("#46 interfaceNames is null or empty, interfaceNames=" + interfaceNames);
                continue;
            }

            String interfaceLine = interfaceNames;

            if (!(beanInstance instanceof EchoService)) {
                continue;
            }

            if (beanInstance instanceof GenericService) {
                continue;
            }

            if (ReflectUtil.isAssignableFrom(REGISTRY_SERVICE, beanInstance.getClass())) {
                log.info("#161 bean is RegistryService, " + beanName + ", " + beanInstance);
                continue;
            }

            // 不echo
            if (null != echo && "false".equalsIgnoreCase(echo)) {
                lines.add(interfaceLine + getVersion(beanFactory, beanName) + "   no echo");
                continue;
            }

            // echo
            try {
                Object $echo = ((EchoService) beanInstance).$echo("echo success");

                lines.add(interfaceLine + getVersion(beanFactory, beanName) + "   " + $echo);
            } catch (Throwable e) {
                log.error("#73 writeDubboEchoResponse error, " + e, e);

                if (StringUtil.contains("" + e, "No provider available")) {
                    lines.add(interfaceLine + getVersion(beanFactory, beanName) + "   " + "<b><span title=\"" + e + "\">No provider available</span></b>");
                } else {
                    lines.add(interfaceLine + getVersion(beanFactory, beanName) + "   " + "<b>" + e + "</b>");
                }
            }
        }

        if (null == lines || lines.isEmpty()) {
            return "<h1>no dubbo reference</h1>";
        }

        String response = "<pre>";
        for (int i = 0; i < lines.size(); i++) {
            response += (i + 1) + ". " + lines.get(i) + "\n";
        }
        response += "</pre>";
        return response;
    }

    private static ConfigurableListableBeanFactory getBeanFactory() {
        BudoApplicationContextAware aware = BudoApplicationContextAware.getInstance();
        if (null == aware) {
            return null;
        }

        AbstractBudoConfigProcessor configProcessor = SpringUtil.getBean(aware.getApplicationContext(), AbstractBudoConfigProcessor.class);
        if (null == configProcessor) {
            return null;
        }

        ConfigurableListableBeanFactory beanFactory = (ConfigurableListableBeanFactory) configProcessor.getBeanFactory();
        return beanFactory;
    }

    private static String getVersion(ConfigurableListableBeanFactory beanFactory, String beanName) {
        if (null == beanFactory || null == beanName) {
            return ":NO_VERSION";
        }

        BeanDefinition beanDefinition = beanFactory.getMergedBeanDefinition(beanName);

        MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
        PropertyValue versionProperty = propertyValues.getPropertyValue("version");
        Object versionConverted = versionProperty.getConvertedValue();

        return ":" + versionConverted;
    }

    protected static List<String> getBeanInterfaceNames(Object beanInstance) {
        if (null == beanInstance) {
            return null;
        }

        Class<?> beanType = beanInstance.getClass();
        List<Class<?>> allInterfaces = ReflectUtil.getAllInterfaces(beanType);
        List<String> interfaceNames = new ArrayList<String>();
        for (Class<?> iface : allInterfaces) {
            String interfaceName = iface.getName();
            if (interfaceName.startsWith("org.springframework") //
                    || interfaceName.startsWith("com.alibaba.dubbo") //
                    || interfaceName.startsWith("java.io.Serializable")) {
                continue;
            }
            interfaceNames.add(interfaceName);
        }
        return interfaceNames;
    }

    public static String writeDubboReference(ApplicationContext applicationContext, String url) {
        Set<Map<String, Object>> referenceSet = new HashSet<Map<String, Object>>();
        String[] beanNames = applicationContext.getBeanDefinitionNames();
        for (String beanName : beanNames) {
            Object beanInstance = SpringUtil.getBean(applicationContext, beanName, true);
            if (null == beanInstance) {
                continue;
            }

            if (!(beanInstance instanceof EchoService)) {
                continue;
            }

            if (beanInstance instanceof GenericService) {
                continue;
            }

            if (ReflectUtil.isAssignableFrom(beanInstance.getClass().getName(), BudoGraphConfigurer.REGISTRY_SERVICE_TYPE)) {
                log.info("#161 bean is RegistryService, " + beanName + ", " + beanInstance);
                continue;
            }

            List<String> beanInterfaceNames = getBeanInterfaceNames(beanInstance);
            String interfaceNames = beanInterfaceNames + "";
            if (null == interfaceNames || interfaceNames.trim().isEmpty() || "null".equalsIgnoreCase(interfaceNames)) {
                log.error("#46 interfaceNames is null or empty, interfaceNames=" + interfaceNames);
                continue;
            }

            String interfaceName = beanInterfaceNames.get(0);
            referenceSet.add(MapUtil.stringObjectMap("interface", interfaceName));
        }

        return JSONUtils.toJSONString(referenceSet);
    }
}